﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Windows.Foundation;
using Windows.Foundation.Collections;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Controls.Primitives;
using Windows.UI.Xaml.Data;
using Windows.UI.Xaml.Input;
using Windows.UI.Xaml.Media;
using Windows.UI.Xaml.Navigation;
using Windows.UI.Xaml.Shapes;
using Windows.UI;
using Windows.UI.Input;

// The Basic Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234237

namespace MyModeler
{
    /// <summary>
    /// A basic page that provides characteristics common to most applications.
    /// </summary>
    public sealed partial class FreeDrawingPage : MyModeler.Common.LayoutAwarePage
    {
        public FreeDrawingPage()
        {
            this.InitializeComponent();
            this.canvas1.PointerPressed += new PointerEventHandler(this.canvas1_PointerPressed);
            this.canvas1.PointerReleased += new PointerEventHandler(this.canvas1_PointerReleased);
            this.canvas1.PointerMoved += new PointerEventHandler(this.canvas1_PointerMoved);
            this.lstTools.SelectionChanged += new SelectionChangedEventHandler(this.lstTools_SelectionChanged);

            m_objects = new List<CElement>();
            m_selection = new List<CElement>();
            
            m_objectId = String.Empty;
	        m_lastPoint = new Point(0, 0);

	        // Initialize Current UI interaction members
	        m_bDrawing = false;
	        // Current selected drawing tool = SELECT
	        m_type = ElementType.type_select;
	        // Current selected shape type from Ribbon = type_unknown
	        m_shapeType = ShapeType.unknown;
            m_nDragHandle = 1;

            InitTools();
        }

        private void InitTools()
        {
            lstTools.Items.Add(" Select...");
            lstTools.Items.Add(" Write Text");
            lstTools.Items.Add(" Draw Rectangle");
            lstTools.Items.Add(" Draw Ellipse");
            lstTools.Items.Add(" Draw Line");
            lstTools.Items.Add(" Draw Line Broken Right");
            lstTools.Items.Add(" Draw Line Horizontal");
            lstTools.Items.Add(" Draw Line Vertical");
            lstTools.Items.Add(" Draw Arrow Right");
            lstTools.Items.Add(" Draw Arrow Down");
            lstTools.Items.Add(" Draw Line");
            lstTools.Items.Add(" Draw Line");
            lstTools.Items.Add(" Draw Line");
            lstTools.Items.Add(" Draw Picture");

            SwitchInSelectMode();
        }

        private void SwitchInSelectMode()
        {
            lstTools.SelectedIndex = 0;
        }

        void lstTools_SelectionChanged(object sender, SelectionChangedEventArgs e)
        {
            string tool = (string)lstTools.SelectedItem;
            if( tool.Contains("Select") == true )
            {
                m_type = ElementType.type_select;
                m_shapeType = ShapeType.unknown;
            }
            else if (tool.Contains("Rectangle") == true)
            {
                m_type = ElementType.type_shapes_simple;
                m_shapeType = ShapeType.rectangle;
            }
            else if (tool.Contains("Ellipse") == true)
            {
                m_type = ElementType.type_shapes_simple;
                m_shapeType = ShapeType.ellipse;
            }
            else if (tool.Contains("Line") == true)
            {
                m_type = ElementType.type_shapes_simple;
                m_shapeType = ShapeType.line;
            }
            else if (tool.Contains("Write Text") == true)
            {
                m_type = ElementType.type_text;
                m_shapeType = ShapeType.text;
            }
            else
            {
                m_type = ElementType.type_unknown;
                m_shapeType = ShapeType.unknown;
            }
        }

        /// <summary>
        /// Invoked when this page is about to be displayed in a Frame.
        /// </summary>
        /// <param name="e">Event data that describes how this page was reached.  The Parameter
        /// property is typically used to configure the page.</param>
        protected override void OnNavigatedTo(NavigationEventArgs e)
        {
        }

        private void btnAdd_Click(object sender, RoutedEventArgs e)
        {
            int i = lstTools.SelectedIndex;
            string tool = (string) lstTools.SelectedItem;

            LogDebug("selected tool = " + tool);

        }

        private void btnClear_Click(object sender, RoutedEventArgs e)
        {
            canvas1.Children.Clear();
        }

        private void btnProperties_Click(object sender, RoutedEventArgs e)
        {
            bool existsSelection = true;
            if (existsSelection == true)
            {
                this.Frame.Navigate(typeof(PropertiesPage));
            }
        }

        private void btnUpdateProperties_Click(object sender, RoutedEventArgs e)
        {
            string objectId = txtObjectId.Text;
            UpdateFromPropertyGrid(objectId);            
        }

        private void canvas1_PointerPressed(object sender, PointerEventArgs e)
        {
            string str;
            //LogDebug("canvas1_PointerPressed");

	        if( m_type == ElementType.type_unknown )
	        {
		        //FIXME : do we need to handle not implemented objects ?
		        return;
	        }
	
            PointerPoint pt = e.GetCurrentPoint(canvas1);
            Point point = pt.Position;
            double x = pt.Position.X;
            double y = pt.Position.Y;
            // Debugging
            str = String.Format("canvas1_PointerPressed at {0},{1}", x, y);
            LogDebug(str);

            m_lastPoint = point;
            
            if (m_type == ElementType.type_select)
	        {

		        m_selectMode = SelectMode.none;

		        // Check for resizing (only allowed on single selections)
		        if( HasSelection() &&  m_selection.Count == 1)
		        {
			        CElement pElement = m_selection[0];
			        // Change cursor look because mouse click is over an object for sizing 
                    m_nDragHandle = pElement.HitTest(point);
                    if (m_nDragHandle != 0)
                    {
                        m_selectMode = SelectMode.size;
                    }
		        }

		        if( m_selectMode == SelectMode.none )
		        {
			        // See if the click was on an object
			        CElement pElement = ObjectAt(point);
			        if( pElement != null )
			        {
				        if( HasSelection() )
				        {
					        LogDebug("selection cleared");
					        SelectNone();
				        }

				        LogDebug("object found ->" + pElement.ToString());
                        if( IsSelected(pElement) == false )
                        {
                            LogDebug("object selected ->" + pElement.ToString());
                            m_objectId = pElement.m_objectId;
                            Select(pElement);
                            pElement.m_bMoving = true;
                            m_selectMode = SelectMode.move;
                        }

				        // Update UI
				        UpdateUI(pElement);
                        // Redraw
                        Invalidate();
			        }
			        else
			        {
				        // See if the click was on an object
				        // TRUE -> select and start move if so
				        // FALSE -> Click on background, start a net-selection
				        // m_selectMode = netSelect;

				        if( HasSelection() )
				        {
					        LogDebug("selection cleared");
					        SelectNone();
					        //UpdateUI();
                            Invalidate();
				        }
			        }
		        }
	        }
	        // We are not in a select operation
	        // -> this is a drawing operation
	        // We have to create...
	        // Create a Drawable Object...
	        else
	        {
		        LogDebug("selection cleared");
		        SelectNone();

		        CElement newElement = CFactory.CreateElementOfType(m_type, m_shapeType);
		        if( newElement.m_type == ElementType.type_unknown )
		        {
			        LogDebug("object not implemented yet ! ->" + newElement.ToString());
			        return;
		        }

                newElement.m_point = point;
                // For plumbing purpose...
                //newElement->m_pManager = this;
                // Add an object
                m_objects.Add(newElement);
                LogDebug("object created ->" + newElement.ToString());

                // Store last created object
                m_objectId = newElement.m_objectId;

                // Select the new element
		        Select(newElement);
		        LogDebug("object selected ->" + newElement.ToString());

		        m_selectMode = SelectMode.size;

                UpdateUI(newElement);
            }
            e.Handled = true;
        }

        private void canvas1_PointerMoved(object sender, PointerEventArgs e)
        {
            string str;
            //LogDebug("canvas1_PointerMoved");

            PointerPoint pt = e.GetCurrentPoint(canvas1);
            Point point = pt.Position;
            double x = pt.Position.X;
            double y = pt.Position.Y;
	        // Debugging
            str = String.Format("canvas1_PointerMoved at {0},{1}", x, y);
            //LogDebug(str);

            // a SELECT operation is started
	        if( m_selectMode == SelectMode.none )
	        {
	        }
	
	        //if( m_bDrawing == FALSE )
	        //	return;

	        CElement pElement = FindElement(m_objects, m_objectId);
	        if( pElement == null )
		        return;

	        if( m_type == ElementType.type_select )
	        {
		        if( HasSelection() &&  m_selection.Count == 1)
		        {
                    //// Change cursor look temporary just because mouse could be over a shape
                    //int nHandle = pElement->HitTest(point, pView, true);
                    //if (nHandle != 0)
                    //{
                    //    SetCursor(pElement->GetHandleCursor(nHandle));
                    //}


			        if( m_selectMode == SelectMode.move )
			        {
				        LogDebug("object selection moved ->" + pElement.ToString());

                        CElement pObj = m_selection[0];
                        //Point delta = point - m_lastPoint;
                        double deltaX = point.X - m_lastPoint.X;
                        double deltaY = point.Y - m_lastPoint.Y;
                        ////pObj->m_rect += delta;
                        ////pObj->m_point = pObj->m_rect.TopLeft();
                        pObj.m_rect.X += deltaX;
                        pObj.m_rect.Y += deltaY;
                        //pObj.m_rect.Width += deltaX;
                        //pObj.m_rect.Height += deltaY;
                        pObj.m_point.X = pObj.m_rect.Left;
                        pObj.m_point.Y = pObj.m_rect.Top;
				        //UpdateUI();
                        Invalidate();
			        }

                    if( m_selectMode == SelectMode.size )
                    {
                        if( m_nDragHandle == 5 ) //!= 0)
                        {
                            LogDebug("object selection sized ->" + pElement.ToString());

                            CElement pObj = m_selection[0];
                            //Rect r = pObj.m_rect;
                            //r.Right = point.X;
                            //r.Bottom = point.Y;
                            double deltaX = point.X - m_lastPoint.X;
                            double deltaY = point.Y - m_lastPoint.Y;
                            pObj.m_rect.Width += deltaX;
                            pObj.m_rect.Height += deltaY;
                            pObj.m_point.X = pObj.m_rect.Left;
                            pObj.m_point.Y = pObj.m_rect.Top;
                            //UpdateUI();
                            Invalidate();
                        }
                    }
		        }
	        }
	        else
	        {
		        if( m_selectMode == SelectMode.size )
		        {
			        LogDebug("object is under drawing ->" + pElement.ToString());
		
			        pElement.m_last = point;
			        pElement.InvalidateObj();
			        //UpdateUI();
                    Invalidate();
		        }
	        }	

	        m_lastPoint = point;

	        // Check for mouse cursor -> sizing/moving
            //if( m_selectMode == SelectMode.size )
            //{
            //    //if (m_nDragHandle != 0)
            //    {
            //        SetCursor(m_selection.GetHead()->GetHandleCursor(m_nDragHandle));
            //    }
            //}

            //if( m_type == ElementType::type_select && m_selection.GetCount() == 0)
            //{
            //    SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW));
            //}
            e.Handled = true;
        }

        private void canvas1_PointerReleased(object sender, PointerEventArgs e)
        {
            string str;
            //LogDebug("canvas1_PointerReleased");

            PointerPoint pt = e.GetCurrentPoint(canvas1);
            Point point = pt.Position;
            double x = pt.Position.X;
            double y = pt.Position.Y;
	        // Debugging
            str = String.Format("canvas1_PointerReleased at {0},{1}", x, y);
            LogDebug(str);

	        //if( m_selectMode == SelectMode::move || m_selectMode == SelectMode::size )
	        {
		        // Selection Moving or Sizing is finished. 
		        // Nothing to do.
		        m_selectMode = SelectMode.none;
	        }

	        //m_bDrawing = FALSE;

	        CElement pElement = FindElement(m_objects, m_objectId);
	        if( pElement == null )
		        return;	
	
	        if( m_type == ElementType.type_select )
	        {
		        if( HasSelection() &&  m_selection.Count == 1)
		        {
			        // Nothing to do...
			        LogDebug("object selection finished ->" + pElement.ToString());
		        }
	        }
	        else
	        {
		        // Finish a drawing operation
		        pElement.m_last = point;
		        pElement.InvalidateObj();
		        pElement.CheckForKeepingAMinimumSize();
		        
                // Switch the view in Select Mode
                m_type = ElementType.type_select;
                SwitchInSelectMode();

		        LogDebug("object drawing finished ->" + pElement.ToString());
	        }

	        pElement.m_bMoving = false;
	        // Update UI
	        UpdateUI(pElement);
            e.Handled = true;
        }

        private CElement FindElement(List<CElement> list, string objectId)
        {
            foreach (CElement el in list)
            {
                if (el.m_objectId == objectId)
                    return el;
            }
            return null;
        }

        private bool IsSelected(CElement pElement)
        {
            CElement obj = FindElement(m_selection, pElement.m_objectId);
            if (obj == null)
                return false;
            else
                return true;
        }

        private CElement ObjectAt(Point point)
        {
            Point p1 = point;
            Point p2 = new Point(point.X + 5, point.Y + 5);
            Rect rect = new Rect(p1, p2);
            foreach (CElement el in m_objects)
            {
                Rect r = el.m_rect;
                if (PointInRect(point, r) == true)
                {
                    return el;
                }
               
            }
            return null;
        }

        private bool PointInRect(Point point, Rect r)
        {
            if (r.Left <= point.X && point.X <= r.Right && r.Top <= point.Y && point.Y <= r.Bottom)
            {
                return true;
            }
            else
                return false;
        }

        private bool HasSelection()
        {
            return m_selection.Count > 0;
        }

        private void SelectNone()
        {
            m_selection.Clear();
        }

        private void Select(CElement newElement)
        {
            m_selection.Add(newElement);
        }

        private void Invalidate()
        {
            canvas1.Children.Clear();
            foreach (CElement el in m_objects)
            {
                el.Draw(canvas1);
                if (IsSelected(el))
                    el.DrawTracker(canvas1, TrackerState.selected);
            }
        }

        private void UpdateUI(CElement pElement)
        {
            // Properties view is simple : only text boxes...
            string str;
            str = String.Format("L={0},T={1},R={2},B={3}",
                Convert.ToInt32(pElement.m_rect.Left), 
                Convert.ToInt32(pElement.m_rect.Top), 
                Convert.ToInt32(pElement.m_rect.Right), 
                Convert.ToInt32(pElement.m_rect.Bottom));
            txtPosition.Text = str;
            txtName.Text = pElement.m_name;
            txtObjectId.Text = pElement.m_objectId;
            txtType.Text = pElement.m_shapeType.ToString();
            txtText.Text = pElement.m_text;
            txtFillColor.Text = pElement.m_colorFill.ToString();
            txtLineColor.Text = pElement.m_colorLine.ToString();
            chkHasFillColor.IsChecked = pElement.m_bColorFill;
            chkHasLineColor.IsChecked = pElement.m_bColorLine;
            chkIsSolidFillColor.IsChecked = pElement.m_bSolidColorFill;
        }

        private void UpdateFromPropertyGrid(string objectId)
        {
            CElement pElement = FindElement(m_objects, objectId);
            if (pElement == null)
                return;
            pElement.m_name = txtName.Text;
            pElement.m_text = txtText.Text;
            pElement.m_bColorFill = chkHasFillColor.IsChecked.Value;
            pElement.m_bColorLine = chkHasLineColor.IsChecked.Value;
            pElement.m_bSolidColorFill = chkIsSolidFillColor.IsChecked.Value;

            // Redraw
            Invalidate();
        }

        private void LogDebug(string message)
        {
            if (txtDebug.Text.Length > 2000)
                txtDebug.Text = String.Empty;

            txtDebug.Text += "\n" + message;
        }

        //
        // Attributes Current UI interaction members
        //
        // Drawing objects
        public List<CElement> m_objects;
        // Selection objects
        public List<CElement> m_selection;
        public Point m_lastPoint;
        // Current working object
        public String m_objectId;
        // Current Select action
        public SelectMode m_selectMode;
        // Cursor hanlde
        int m_nDragHandle;
    	// Is in drawing...
	    public bool m_bDrawing;
	    // Current selected drawing tool
	    public ElementType m_type;
	    // Current selected shape type from Ribbon
	    public ShapeType m_shapeType;

    }
}
